Repository navigation
Conversation
Adds schema/2026-07-28 to the sync script and re-vendors every version at 5f5440bb (the 2026-07-28 tag). draft and 2026-07-28 are identical there apart from doc links; the dated 2025-06-18/2025-11-25 schemas pick up the upstream NumberSchema generator fix (#3139).
…sion after it
2026-07-28 shipped, but the harness still modelled it as the draft:
LATEST_SPEC_VERSION was 2025-11-25 and DRAFT_PROTOCOL_VERSION doubled as
both the draft's wire string and its timeline identity. Now that the spec
repo's draft declares the same wire version as the release, those two roles
have to be separate things.
- DATED_SPEC_VERSIONS gains 2026-07-28 and LATEST_SPEC_VERSION points at it.
- DRAFT_SPEC_VERSION ('draft') is the timeline id after the latest release;
DRAFT_PROTOCOL_VERSION stays the draft's wire string (still 2026-07-28,
mirroring schema/draft/schema.ts). protocolVersionFor() maps a SpecVersion
to its wire string and is used wherever a version goes on the wire
(headers, _meta, supportedVersions, MCP_CONFORMANCE_PROTOCOL_VERSION).
- Without --spec-version each scenario now runs at the latest release if it
applies there, else the newest revision in its window (defaultSpecVersionFor),
so stateful-only scenarios keep the initialize wire and nothing is handed a
lifecycle it cannot speak.
- --spec-version draft and --suite draft still parse; each prints a one-line
note (2026-07-28 is released / the draft suite is currently empty).
- Wire-schema validation routes 2026-07-28 to its own vendored schema and
drops the NumberSchema erratum the re-vendored dated schemas no longer need.
- sdk-runner: specOverrides keys are dated versions; a draft run resolves its
overlay through the draft's wire version.
- Test helpers default to 2025-11-25 (what the bundled SDK client/server
speak) now that the latest release is stateless.
…rom predicates Every scenario tagged introducedIn/removedIn: DRAFT_PROTOCOL_VERSION covers a requirement that shipped (or was removed) in 2026-07-28, so tag them with the date. They join the default suites (active, auth, core) and tier scoring; the draft suites are left for genuinely unreleased requirements and are empty. In-scenario 'is this the draft?' checks become era/feature predicates so the next revision inherits them: isStateless(ctx) for lifecycle, specVersionAtLeast (v, '2026-07-28') for SEP-2106/2350/837 gating, and protocolVersionFor() for hand-built headers, _meta and mock supportedVersions. Scenario imports of spec-types/draft move to spec-types/2026-07-28. withRequiredDraftResultFields is renamed withRequiredResultFields; the vestigial specVersions field on the input-required-result scenarios is dropped. The example client keys its lifecycle off the wire version list and defaults to the latest release.
…equirement set Fixes #426. With 2026-07-28 dated, its scenarios count toward the tier matrix; only genuinely-draft and extension scenarios stay informational, and a scored scenario that also applies at the draft no longer shows up a second time in the informational columns. In URL mode with neither --requirements nor --spec-version, tier-check used to run the client leg as an unfiltered --suite all, which put the removed 2025-03-26 backcompat scenarios in the denominator and ran them on the wrong wire. It now scores against the latest shipped revision's frozen requirement set (requirements/2026-07-28.yaml) and says so on stderr; --requirements claims more revisions, --spec-version keeps the unfrozen per-version path.
README/--help/skill wording that described 2026-07-28 as 'the draft' now names it as a dated release, explains what --spec-version draft and --suite draft mean after a release, and documents the new no-flag defaults (per-scenario wire selection; tier-check scoring the latest requirement set). AGENTS.md gains a line on how to tag introducedIn for shipped vs draft-only requirements.
Tests that used DRAFT_PROTOCOL_VERSION to mean 'the stateless revision' pin '2026-07-28' explicitly, tests that relied on LATEST_SPEC_VERSION being stateful pin '2025-11-25', and the draft-suite/alias expectations follow the new DRAFT_SPEC_VERSION identity. New coverage: defaultSpecVersionFor, protocolVersionFor at the header/_meta/env boundaries under --spec-version draft, the draft→wire specOverrides lookup, and LATEST_SPEC_VERSION having a frozen requirement set. all-scenarios runs each fixture scenario at its default revision and again on 2025-11-25 when it spans both wires. draft-result-fields.test.ts is renamed required-result-fields.test.ts.
Promoting 2026-07-28 touched the schema list in three places and every help string that named a date. Derive them instead, so the next release is 'append the date to DATED_SPEC_VERSIONS, run sync-schema' plus the inherently per-release work (retag shipped scenarios, freeze a requirement set): - LATEST_SPEC_VERSION is the last DATED_SPEC_VERSIONS entry, not a second constant. - DRAFT_PROTOCOL_VERSION is read from the vendored schema/draft/schema.ts, so a draft marker bump upstream is just `npm run sync-schema`. - sync-schema vendors DATED_SPEC_VERSIONS + draft (no separate list) and generates src/spec-types/schemas.ts, the version -> JSON Schema map the wire validator imports; wire-schema.ts no longer hardcodes imports per version. - --spec-version/--requirements help and error text interpolate the version list instead of naming dates. - Tests that pinned exact list contents assert structure instead. - src/spec-types/README.md gains the spec lifecycle checklist (draft marker bump / new draft-only scenario / new release).
|
Cross-SDK matrix for this branch (
Not shown above (40 of 56 before/after pairs have an identical Status cell):
Notes:
Full tables (all 15, unchanged)
|
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 73/73 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 1 failed, 0 warnings / 73 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
✅ 73/73 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
✅ 73/73 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
✅ 73/73 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
✅ 73/73 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
✅ 73/73 checks | ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 151/151 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 1 failed, 0 warnings / 73 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 48 failed, 6 warnings / 135 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
✅ 151/151 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
✅ 73/73 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 48 failed, 5 warnings / 134 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
✅ 151/151 checks | ruby 3.2.3, bundler 2.4.20 |
server --requirements 2026-07-28
before (main)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 30 failed, 0 warnings / 193 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 145 failed, 5 warnings / 156 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 30 failed, 0 warnings / 194 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 25 failed, 0 warnings / 193 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 101 failed, 5 warnings / 156 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 11 failed, 2 warnings / 192 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 38 failed, 0 warnings / 189 checks | ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 30 failed, 0 warnings / 193 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 145 failed, 5 warnings / 156 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 30 failed, 0 warnings / 194 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 25 failed, 0 warnings / 193 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 101 failed, 5 warnings / 156 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 11 failed, 2 warnings / 192 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 38 failed, 0 warnings / 189 checks | ruby 3.2.3, bundler 2.4.20 |
server --requirements 2025-11-25
before (main)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 84/84 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 3 failed, 0 warnings / 81 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
go 1.27.1 | |
python-sdk |
08a3bc8e |
✅ 84/84 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 1 failed, 0 warnings / 78 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
✅ 80/80 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 84/84 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 3 failed, 0 warnings / 81 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
go 1.27.1 | |
python-sdk |
08a3bc8e |
✅ 84/84 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 1 failed, 0 warnings / 78 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
✅ 81/81 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
ruby 3.2.3, bundler 2.4.20 |
client --requirements 2026-07-28
before (main)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 1 failed, 0 warnings / 453 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 60 failed, 0 warnings / 268 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 13 failed, 0 warnings / 457 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 9 failed, 0 warnings / 469 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 64 failed, 0 warnings / 368 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 92 failed, 0 warnings / 104 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 11 failed, 1 warnings / 417 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 9 failed, 0 warnings / 467 checks | ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 1 failed, 0 warnings / 453 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 60 failed, 0 warnings / 268 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 13 failed, 0 warnings / 457 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 9 failed, 0 warnings / 469 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 64 failed, 0 warnings / 368 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 92 failed, 0 warnings / 104 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 11 failed, 1 warnings / 417 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 9 failed, 0 warnings / 467 checks | ruby 3.2.3, bundler 2.4.20 |
client --requirements 2025-11-25
before (main)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 1 failed, 0 warnings / 308 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 13 failed, 0 warnings / 297 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 13 failed, 0 warnings / 285 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 9 failed, 0 warnings / 307 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 14 failed, 0 warnings / 301 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 65 failed, 0 warnings / 65 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 11 failed, 1 warnings / 283 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 9 failed, 0 warnings / 302 checks | ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 1 failed, 0 warnings / 308 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 13 failed, 0 warnings / 297 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 13 failed, 0 warnings / 285 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 9 failed, 0 warnings / 307 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 14 failed, 0 warnings / 301 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 65 failed, 0 warnings / 65 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 11 failed, 1 warnings / 283 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 9 failed, 0 warnings / 302 checks | ruby 3.2.3, bundler 2.4.20 |
client --suite core, no --spec-version
before (main)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 250/250 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
✅ 270/270 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
✅ 263/263 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
✅ 238/238 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 1 failed, 0 warnings / 238 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 50 failed, 0 warnings / 50 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
✅ 229/229 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
✅ 236/236 checks | ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 356/356 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
✅ 270/270 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
✅ 394/394 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
✅ 358/358 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 1 failed, 0 warnings / 238 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 62 failed, 0 warnings / 64 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
✅ 328/328 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
✅ 362/362 checks | ruby 3.2.3, bundler 2.4.20 |
client --suite extensions
before (main)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 1 failed, 0 warnings / 49 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
✅ 0/0 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 13 failed, 0 warnings / 13 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 9 failed, 0 warnings / 60 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
✅ 0/0 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 13 failed, 0 warnings / 13 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 9 failed, 1 warnings / 51 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 9 failed, 0 warnings / 57 checks | ruby 3.2.3, bundler 2.4.20 |
after (this branch)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
❌ 1 failed, 0 warnings / 45 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
✅ 0/0 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
❌ 13 failed, 0 warnings / 13 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
❌ 9 failed, 0 warnings / 59 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
✅ 0/0 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ 13 failed, 0 warnings / 13 checks | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
❌ 9 failed, 1 warnings / 45 checks | dotnet 10.0.400 |
ruby-sdk |
8a24e50 |
❌ 9 failed, 0 warnings / 56 checks | ruby 3.2.3, bundler 2.4.20 |
server --spec-version draft (after only)
| SDK | SDK head | Status | Toolchain |
|---|---|---|---|
typescript-sdk |
5119ee7f |
✅ 119/119 checks | node 22.23.2, pnpm 10.26.1, npm 10.9.8 |
typescript-sdk-v1 |
12b42567 |
❌ 95 failed, 5 warnings / 106 checks | node 22.23.2, pnpm 12.3.4, npm 10.9.8 |
go-sdk |
a5026ea |
✅ 120/120 checks | go 1.27.1 |
python-sdk |
08a3bc8e |
✅ 119/119 checks | uv 0.12.10, python 3.12.3 |
python-sdk-v1 |
8c2fa6ea |
❌ 62 failed, 5 warnings / 106 checks | uv 0.12.10, python 3.12.3 |
rust-sdk |
3023198 |
❌ server failed to start: Server exited with code 127 before tests completed | cargo 1.96.1, rustc 1.96.1 |
csharp-sdk |
df11d7da |
dotnet 10.0.400 | |
ruby-sdk |
8a24e50 |
✅ 119/119 checks | ruby 3.2.3, bundler 2.4.20 |
Collapsed to a before/after diff by Claude Code at the author's request; the original tables are unchanged in the block above.
Bring the branch up to date with main (tools-name-format on 2025-11-25+, deterministic tools/list ordering, the DNS-rebinding initialized notification, RFC 8707 resource parameter, custom-header schema freshness, skills server conformance, DPoP authorization-server scenario, 0.2.0-alpha.12). Conflicts: - src/scenarios/server/dns-rebinding.ts: keep main's initialized notification after an accepted probe (and the session id it forwards), on this branch's version model: the probe still takes (stateless, protocolVersion) from isStateless(ctx) / protocolVersionFor(), and the notification is skipped for the stateless lifecycle via !stateless rather than by comparing against DRAFT_PROTOCOL_VERSION. - src/scenarios/server/tools.ts: keep main's specVersionAtLeast / SpecVersion imports for the name-format and ordering checks; drop the DRAFT_PROTOCOL_VERSION import, since this branch already retagged the three removedIn tags to '2026-07-28'. Main-only code that still assumes DRAFT_PROTOCOL_VERSION is a spec tag is adapted in the next commit. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qo1j9qYJ4u9QvPuwqsaWod
After the merge, code that landed on main since this branch was cut still treated DRAFT_PROTOCOL_VERSION as a spec-timeline tag and imported the renamed withRequiredDraftResultFields, so the tree did not typecheck. - authorization-server/dpop.ts: introducedIn DRAFT_SPEC_VERSION. SEP-1932 did not ship in the 2026-07-28 release: neither the 2026-07-28 changelog nor the vendored 2026-07-28 schema mentions DPoP, the profile is a draft on a modelcontextprotocol/ext-auth branch, and requirements/2026-07-28.yaml lists the client DPoP scenarios as not scored (extension). Keeping it at the draft preserves main's intent that it is not part of a dated revision. - client/skills mock: withRequiredResultFields, and the initialize fallback comes from protocolVersionFor(<run's spec version>) like the other BaseHttpScenario mocks, instead of the draft's wire string. The skills scenarios stay extension-tagged, so no timeline tag changes. - server/skills/enumeration.ts: gate the cache-attribute checks with specVersionAtLeast(v, '2026-07-28') rather than a string comparison, which only ordered 'draft' after the dates by accident. - Tests added on main pin '2026-07-28' / '2025-11-25' where they meant stateless / stateful, matching how this branch converted their neighbours (main's LATEST_SPEC_VERSION meant 2025-11-25 in the DNS-rebinding lifecycle test). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qo1j9qYJ4u9QvPuwqsaWod
URL mode with neither --requirements nor --spec-version now scores against the latest shipped revision's frozen requirement set. That default also fired when there was no conformance leg at all (no --conformance-server-url and no --client-cmd), so a GitHub-only run reported requirements_revisions: ['2026-07-28'] and "Scored against 2026-07-28" although both legs were skipped and nothing was measured. Apply the default only when a server URL or client command is present. An explicit --requirements is unchanged. The new test drives the command with the GitHub checks and conformance legs stubbed and asserts on the printed scorecard. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qo1j9qYJ4u9QvPuwqsaWod
A `draft` run resolves its specOverrides entry through the draft's wire version, and specOverrides keys must be dated versions. That only works while the vendored draft schema still declares the latest release's protocolVersion. As soon as the spec repo gives the draft its own marker (the "step 2 alone" case), the lookup matches nothing and the run silently uses the base invocation: go-sdk would start its stateful-era server and csharp-sdk would be driven at its stateful endpoint for a stateless run. The draft is the revision after the latest release and inherits its era, so when the draft's wire version has no entry, use specOverrides[LATEST_SPEC_VERSION]. The test re-evaluates the modules against a draft schema that declares a non-dated marker, which is the only way to observe the gap today. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qo1j9qYJ4u9QvPuwqsaWod
commit: |
Requested by Paul Carleton · Slack thread
Fixes #426.
Why
The 2026-07-28 specification revision was released on 2026-07-28, but the harness still modelled it as "the draft":
LATEST_SPEC_VERSIONwas2025-11-25,DRAFT_PROTOCOL_VERSION = '2026-07-28'served as both the draft's wire string and its identity on the spec timeline, every SEP-2575/2322/2243/2549/2468 scenario was taggedintroducedIn: DRAFT_PROTOCOL_VERSION, and tier-check filed all of them under "Informational (not scored)". #447 already frozerequirements/2026-07-28.yaml, so the--requirementspath treated the revision as shipped while everything else did not.We still want
draftto be something the suite can represent (the revision after the latest release), but it has to stop being shorthand for "the 2026-07-28 stateless era". The spec repo'sschema/draft/schema.tscurrently declares the sameLATEST_PROTOCOL_VERSION(2026-07-28) as the release, so the draft's identity and its wire string can no longer be one value.The second goal is that the next release should not need a PR this size. See "Next time" below.
What changes
Model (
src/types.ts)DATED_SPEC_VERSIONSgains2026-07-28;LATEST_SPEC_VERSIONis derived as its last entry. 2025-11-25 stays fully supported.DRAFT_SPEC_VERSION = 'draft'is the timeline identity of the unreleased revision after the latest release.SpecVersion = DatedSpecVersion | 'draft'.DRAFT_PROTOCOL_VERSIONis the draft's wireprotocolVersion, now read from the vendoredschema/draft/schema.ts(still2026-07-28today).protocolVersionFor(v)maps aSpecVersionto its wire string and is used everywhere a version goes on the wire: headers,_meta, mocksupportedVersions,MCP_CONFORMANCE_PROTOCOL_VERSION.isStatefulVersion/isStateless,specVersionAtLeast(v, '2026-07-28')) instead of=== DRAFT_PROTOCOL_VERSION, so the next revision inherits it.Scenarios
introducedIn: DRAFT_PROTOCOL_VERSIONtags become'2026-07-28'(the one further tag that arrived from main, the DPoP authorization-server scenario for SEP-1932, becomesDRAFT_SPEC_VERSIONinstead because DPoP is not in the 2026-07-28 release); all 15removedIn: DRAFT_PROTOCOL_VERSIONtags (initialize, sessions, ping, logging/setLevel, resources/subscribe, SSE resumption, elicitation defaults/enums, the sampling/elicitation/logging tool calls) becomeremovedIn: '2026-07-28'. Verified against the 2026-07-28 changelog.activegoes from 31 to 48 scenarios; the eleven 2026-07-28 auth scenarios (SEP-2468iss, SEP-2207 offline access, SEP-2352 migration, resource-mismatch) joinauth/core.--suite draft(client and server) is kept and is currently empty; it prints a one-line note saying so.spec-types/draftmove to the newly vendoredspec-types/2026-07-28.Defaults when
--spec-versionis omittedremovedIn: '2026-07-28'at2025-11-25, the 2025-03-26 backcompat pair at2025-03-26. Previously everything that was not draft-only ran at 2025-11-25. A server or client that only implements the stateful lifecycle should pass--spec-version 2025-11-25.CLI back-compat
--spec-version draftstill parses. It means "the revision after 2026-07-28": the same scenario set as--spec-version 2026-07-28plus any draft-only scenarios (one today: the authorization-serverdpopscenario), run at the draft's wire version (also2026-07-28today). It prints one stderr line noting that 2026-07-28 is a released revision.--suite draftstill parses; empty today, with a one-line note.--spec-version 2026-07-28and--requirements 2026-07-28keep their meaning; the former now selects 37 server scenarios instead of 20 because the 17 formerly-draft scenarios are no longer excluded fromactive.tier-check (#426)
draftandextensionare informational, and a scored scenario that also applies at the draft is not listed a second time under the informational columns.--requirementsnor--spec-versionnow scores against the latest shipped revision's frozen requirement set (requirements/2026-07-28.yaml) and says so on stderr (only when--conformance-server-urlor--client-cmdis given, so a GitHub-only run claims no revision), instead of an unfiltered--suite allclient leg that counted the removed 2025-03-26 backcompat scenarios and ran them on the wrong wire.--requirements 2025-11-25,2026-07-28claims both;--spec-versionkeeps the unfrozen per-version path.--sdkmode is unchanged (every shipped revision).Vendored schema
scripts/sync-schema.tsnow vendors everyDATED_SPEC_VERSIONSentry plusdraftand generatessrc/spec-types/schemas.ts(the version to JSON Schema map the wire validator imports). Everything is re-synced at the2026-07-28tag (5f5440bb), so2026-07-28validates against its own immutable schema. The re-vendored 2025-06-18/2025-11-25 schemas carry the upstream NumberSchema fix (modelcontextprotocol#3139), so the local erratum and its tripwire test are removed.sdk-runner
specOverrideskeys are dated versions; adraftrun resolves its overlay through the draft's wire version, falling back to the latest release's entry when the draft's wire version has none.Docs and tests
--help, the tier-audit skill and AGENTS.md (one new bullet on how to tagintroducedIn) describe 2026-07-28 as released anddraftas the revision after it;src/spec-types/README.mdgains the spec lifecycle checklist. Tests pin'2026-07-28'/'2025-11-25'explicitly where they meant "stateless" / "stateful"; the unit-test context helpers default to 2025-11-25 because that is what the bundled SDK client/server speak.Next time
After this PR the per-release edit set is:
DATED_SPEC_VERSIONS(LATEST_SPEC_VERSIONfollows).npm run sync-schema -- <release tag>(vendors the newschema/<date>, regeneratesschemas.ts;DRAFT_PROTOCOL_VERSIONtracks the vendored draft on its own).DRAFT_SPEC_VERSIONto the date inintroducedIn/removedIn.requirements/<date>.yaml.specOverridesentries for SDKs that need a different server invocation at the new wire.A draft marker bump upstream (e.g.
DRAFT-2026-v2) is step 2 alone. Help text, the draft notes, the stateless version lists, tier-check columns and the wire validator all derive fromDATED_SPEC_VERSIONS.Cross-SDK check (local
sdkmatrix, allKNOWN_SDKS, this branch vsmain)These numbers were measured at 6b8c76f, before
mainwas merged in at 0570f2a; the checksmainadded will have moved the per-check totals.--requirements 2026-07-28and--requirements 2025-11-25, server and client legs: per-SDK, per-scenario results are identical before and after for typescript-sdk, typescript-sdk-v1, python-sdk, python-sdk-v1, go-sdk, csharp-sdk, ruby-sdk (rust-sdk did not build in the environment used, unrelated to this change). The tier path does not move.--mode serverwith no flags: typescript-sdk, python-sdk, ruby-sdk go from 73/73 to 151/151 checks (the 17 stateless scenarios now run); go-sdk and csharp-sdk fail those 17 plus the spanning scenarios because their defaultKNOWN_SDKSserver invocation is the stateful-era one andspecOverrides['2026-07-28']only applies when a version is requested.--spec-version 2026-07-28(ordraft) and--requirementspick the right invocation and are green; see the open question below.--mode client --suite corewith no flags: every Tier 1 client (typescript, go, python, csharp, ruby) passes the enlarged suite at 2026-07-28; the v1 lines stay pinned to 2025-11-25 and are unchanged.--mode server --spec-version draft: typescript 119/119, go 120/120, python 119/119, ruby 119/119, csharp 2 warnings, matching--spec-version 2026-07-28.Full tables are in the first comment.
SDK impact
server/clientwith no--spec-versionnow exercises 2026-07-28 scenarios at the stateless wire by default (spanning scenarios included). SDKs that only implement 2025-11-25, or whose conformance server speaks one era per process, should pin--spec-version(or use--requirements, which most already do). Expected-failures baselines keyed to the old default will see new scenario names.--spec-version draftkeeps working and now scores; switch to--spec-version 2026-07-28at leisure.--suite draftnow runs zero scenarios (with a note); switch to the default suite or--spec-version 2026-07-28.--requirementsusers andtier-check --sdk: no change.Open questions
sdk <name>with no version resolvespecOverrides[LATEST_SPEC_VERSION]so go-sdk/csharp-sdk start their stateless server by default? That would flip which half of the mixed default suite fails for them; the real fix is that a no-flag run against a one-era-per-process server is not meaningful, and--requirementsalready handles it per revision. Left as is here.client --suite authagainst it without--spec-version 2025-11-25reports failures forauth/scope-step-upandauth/scope-retry-limit. The scenarios themselves pass against every SDK client.Not in this PR
specReferences/src/seps/*.yamlURLs still point at/specification/draft/.... Those pages were restructured before the release (authorization split,transportstotransports/streamable-http,utilities/mrtrtopatterns/mrtr, ...), so moving them to/specification/2026-07-28/...is a page-and-anchor remap plus a traceability.json regen, not a rename. Follow-up.AI disclosure
Per the org AI contribution policy: this PR was written primarily by Claude Code (audit of the
draftusages, code changes, tests, the cross-SDK matrix runs and this description), working from my direction on the design (keepdraftas the revision after the latest release, promote 2026-07-28, make the next release a small diff). I have reviewed the changes and the before/after numbers come from real local runs.